package graphics.tools;

import graphics.shapes.Ball;
import graphics.shapes.Rod;

import java.util.ArrayList;

import javax.media.j3d.BoundingBox;
import javax.media.j3d.Transform3D;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;

/**
 * Class for creating an ArrayList of GeoMag rods to fill a GeoMag sphere with
 * six possible rod-combination to attach.
 * 
 * @author: Eirik Vigeland
 * @version: %I%, %G%
 */
public class RodArray extends ArrayList<Rod> {

	private static final long serialVersionUID = 1L;
	private Vector3d origo;
	private int rotate;
	private double angle, angularOffset;
	private Ball parentNode;
	private ArrayList <Rod> attached;
	
	/**
	 * Constructor which fills the ArrayList with a rod at the top and the
	 * bottom, to the right and left and in front and back of a sphere.
	 * 
	 * @param origo
	 *            Vector3d set to the center of the given sphere of attachment.
	 * @param exclude
	 *            INT to describe the already attached rod (if not the
	 *            Rootnode).
	 * @param rotate
	 *            INT to control the axis of rotation.
	 * @param angle
	 *            The angle at which to rotate.
	 * @param attached
	 *            The rods currently attached.
	 */
	public RodArray(Vector3d origo, int exclude, int rotate, double angle,
			Ball parentNode, ArrayList <Rod> attached) {
		this.origo = origo;
		this.rotate = rotate;
		this.angle = angle;
		this.parentNode = parentNode;
		this.attached = attached;
		angularOffset = angle + ShapeData.PERPENDICULAR;

		if (exclude != ShapeData.TOP  && !isAttached(ShapeData.TOP)) 
			add(topRod());
		if (exclude != ShapeData.BOTTOM && !isAttached(ShapeData.BOTTOM)) 
			add(bottomRod());
		if (exclude != ShapeData.RIGHT && !isAttached(ShapeData.RIGHT)) 
			add(rightRod());
		if (exclude != ShapeData.LEFT && !isAttached(ShapeData.LEFT)) 
			add(leftRod());
		if (exclude != ShapeData.FRONT && !isAttached(ShapeData.FRONT)) 
			add(frontRod());
		if (exclude != ShapeData.BACK && !isAttached(ShapeData.BACK))
			add(backRod());
	}
	
	/**
	 * Checks if a rod is already attached
	 * @param rodPlacement The placement of the rod to be checked
	 * @return True if the rod is attached
	 */
	private boolean isAttached (int rodPlacement){
		boolean value = false;
		for (int i=0; i<attached.size(); i++){
			if (attached.get(i).getPositionOfRod() == rodPlacement)
				value = true;
		}
		return value;
	}

	/**
	 * Setting the properties for the Top rod
	 * @return The top rod
	 */
	private Rod topRod() {
		Rod rod = new Rod(rotate, parentNode);
		Transform3D tr = rod.getTransform3D();
		Transform3D tr2 = rod.getTransform3D();
		Vector3d v3 = new Vector3d();
		double displacement;
		v3.set(origo);

		if (rotate == ShapeData.ROTATE_X) {
			displacement = v3.getY()
					+ ShapeData.angleOffset(angularOffset, ShapeData.SIN);
			v3.setY(displacement);
			displacement = v3.getZ()
					- ShapeData.angleOffset(angularOffset, ShapeData.COS);
			v3.setZ(displacement);
			tr.setTranslation(v3);
			tr2.rotX(angle);
			tr.mul(tr2);
		}

		if (rotate == ShapeData.ROTATE_Y || rotate == ShapeData.ROTATE_DISABLE) {
			displacement = v3.getY() + ShapeData.C_OFFSET;
			v3.setY(displacement);
			tr2.rotY(ShapeData.PERPENDICULAR);
			tr.setTranslation(v3);
			tr.mul(tr2);
		}

		if (rotate == ShapeData.ROTATE_Z) {
			displacement = v3.getX()
					- ShapeData.angleOffset(angle, ShapeData.SIN);
			v3.setX(displacement);
			displacement = v3.getY()
					+ ShapeData.angleOffset(angle, ShapeData.COS);
			v3.setY(displacement);
			tr2.rotZ(angle);
			tr.setTranslation(v3);
			tr.mul(tr2);
		}

		BoundingBox bounds = new BoundingBox(new Point3d(-ShapeData.RODRADIUS,
				-(ShapeData.RODLENGTH * 0.355d), -ShapeData.RODRADIUS),
				new Point3d(ShapeData.RODRADIUS,
						(ShapeData.RODLENGTH * 0.355d), ShapeData.RODRADIUS));
		bounds.transform(tr);

		rod.getTransformGroup().setTransform(tr);
		rod.setPositionOfRod(ShapeData.TOP);
		rod.setAngle(angle);
		rod.setShapeBounds(bounds);
		rod.colorColliding();
		
		return rod;
	}

	
	/**
	 * Setting the properties for the Bottom rod
	 * @return The bottom rod
	 */
	private Rod bottomRod() {
		Rod rod = new Rod(rotate, parentNode);
		Transform3D tr = rod.getTransform3D();
		Transform3D tr2 = rod.getTransform3D();
		Vector3d v3 = new Vector3d();
		double displacement;
		v3.set(origo);

		if (rotate == ShapeData.ROTATE_X) {
			displacement = v3.getY()
					- ShapeData.angleOffset(angularOffset, ShapeData.SIN);
			v3.setY(displacement);
			displacement = v3.getZ()
					+ ShapeData.angleOffset(angularOffset, ShapeData.COS);
			v3.setZ(displacement);
			tr.setTranslation(v3);
			tr2.rotX(angle);
			tr.mul(tr2);
		}

		if (rotate == ShapeData.ROTATE_Y || rotate == ShapeData.ROTATE_DISABLE) {
			displacement = v3.getY() - ShapeData.C_OFFSET;
			v3.setY(displacement);
			tr2.rotY(ShapeData.PERPENDICULAR);
			tr.setTranslation(v3);
			tr.mul(tr2);
		}

		if (rotate == ShapeData.ROTATE_Z) {
			displacement = v3.getX()
					+ ShapeData.angleOffset(angle, ShapeData.SIN);
			v3.setX(displacement);
			displacement = v3.getY()
					- ShapeData.angleOffset(angle, ShapeData.COS);
			v3.setY(displacement);
			tr2.rotZ(angle);
			tr.setTranslation(v3);
			tr.mul(tr2);
		}

		BoundingBox bounds = new BoundingBox(new Point3d(-ShapeData.RODRADIUS,
				-(ShapeData.RODLENGTH * 0.355d), -ShapeData.RODRADIUS),
				new Point3d(ShapeData.RODRADIUS,
						(ShapeData.RODLENGTH * 0.355d), ShapeData.RODRADIUS));
		bounds.transform(tr);
		rod.getTransformGroup().setTransform(tr);
		rod.setPositionOfRod(ShapeData.BOTTOM);
		rod.setAngle(angle);
		rod.setShapeBounds(bounds);
		rod.colorColliding();
		
		return rod;
	}

	/**
	 * Setting the properties for the Right rod
	 * @return The right rod
	 */
	private Rod rightRod() {
		Rod rod = new Rod(rotate, parentNode);
		Transform3D tr = rod.getTransform3D();
		Transform3D tr2 = rod.getTransform3D();
		Vector3d v3 = new Vector3d();
		double displacement;
		v3.set(origo);
		
		if (rotate == ShapeData.ROTATE_X || rotate == ShapeData.ROTATE_DISABLE) {
			displacement = v3.getX() + ShapeData.C_OFFSET;
			v3.setX(displacement);
			tr2.rotZ(ShapeData.PERPENDICULAR);
			tr.setTranslation(v3);
			tr.mul(tr2);
		}

		if (rotate == ShapeData.ROTATE_Y) {
			displacement = v3.getX()
					+ ShapeData.angleOffset(angle, ShapeData.COS);
			v3.setX(displacement);
			displacement = v3.getZ()
					- ShapeData.angleOffset(angle, ShapeData.SIN);
			v3.setZ(displacement);
			Transform3D tr3 = rod.getTransform3D();
			tr3.rotZ(ShapeData.PERPENDICULAR);
			tr2.rotY(angle);
			tr2.mul(tr3);
			tr.setTranslation(v3);
			tr.mul(tr2);
		}

		if (rotate == ShapeData.ROTATE_Z) {
			displacement = v3.getX()
					+ ShapeData.angleOffset(angularOffset, ShapeData.SIN);
			v3.setX(displacement);
			displacement = v3.getY()
					- ShapeData.angleOffset(angularOffset, ShapeData.COS);
			v3.setY(displacement);
			tr.setTranslation(v3);
			tr2.rotZ(angularOffset);
			tr.mul(tr2);
		}

		BoundingBox bounds = new BoundingBox(new Point3d(-ShapeData.RODRADIUS,
				-(ShapeData.RODLENGTH * 0.355d), -ShapeData.RODRADIUS),
				new Point3d(ShapeData.RODRADIUS,
						(ShapeData.RODLENGTH * 0.355d), ShapeData.RODRADIUS));
		bounds.transform(tr);
		rod.getTransformGroup().setTransform(tr);
		rod.setPositionOfRod(ShapeData.RIGHT);
		rod.setAngle(angle);
		rod.setShapeBounds(bounds);
		rod.colorColliding();
		
		return rod;
	}

	/**
	 * Setting the properties for the left rod
	 * @return The left rod
	 */
	private Rod leftRod() {
		Rod rod = new Rod(rotate, parentNode);
		Transform3D tr = rod.getTransform3D();
		Transform3D tr2 = rod.getTransform3D();
		Vector3d v3 = new Vector3d();
		double displacement;
		v3.set(origo);

		if (rotate == ShapeData.ROTATE_X || rotate == ShapeData.ROTATE_DISABLE) {
			displacement = v3.getX() - ShapeData.C_OFFSET;
			v3.setX(displacement);
			tr2.rotZ(ShapeData.PERPENDICULAR);
			tr.setTranslation(v3);
			tr.mul(tr2);
		}

		if (rotate == ShapeData.ROTATE_Y) {
			displacement = v3.getX()
					- ShapeData.angleOffset(angle, ShapeData.COS);
			v3.setX(displacement);
			displacement = v3.getZ()
					+ ShapeData.angleOffset(angle, ShapeData.SIN);
			v3.setZ(displacement);
			Transform3D tr3 = rod.getTransform3D();
			tr3.rotZ(ShapeData.PERPENDICULAR);
			tr2.rotY(angle);
			tr.setTranslation(v3);
			tr2.mul(tr3);
			tr.mul(tr2);
		}

		if (rotate == ShapeData.ROTATE_Z) {
			displacement = v3.getX()
					- ShapeData.angleOffset(angularOffset, ShapeData.SIN);
			v3.setX(displacement);
			displacement = v3.getY()
					+ ShapeData.angleOffset(angularOffset, ShapeData.COS);
			v3.setY(displacement);
			tr.setTranslation(v3);
			tr2.rotZ(angularOffset);
			tr.mul(tr2);
		}

		BoundingBox bounds = new BoundingBox(new Point3d(-ShapeData.RODRADIUS,
				-(ShapeData.RODLENGTH * 0.355d), -ShapeData.RODRADIUS),
				new Point3d(ShapeData.RODRADIUS,
						(ShapeData.RODLENGTH * 0.355d), ShapeData.RODRADIUS));
		bounds.transform(tr);
		rod.getTransformGroup().setTransform(tr);
		rod.setPositionOfRod(ShapeData.LEFT);
		rod.setAngle(angle);
		rod.setShapeBounds(bounds);
		rod.colorColliding();
		
		return rod;
	}

	/**
	 * Setting the properties for the front rod
	 * @return The front rod
	 */
	private Rod frontRod() {
		Rod rod = new Rod(rotate, parentNode);
		Transform3D tr = rod.getTransform3D();
		Transform3D tr2 = rod.getTransform3D();
		Vector3d v3 = new Vector3d();
		double displacement;
		v3.set(origo);

		if (rotate == ShapeData.ROTATE_X) {
			displacement = v3.getY()
					- ShapeData.angleOffset(angle, ShapeData.SIN);
			v3.setY(displacement);
			displacement = v3.getZ()
					+ ShapeData.angleOffset(angle, ShapeData.COS);
			v3.setZ(displacement);
			tr2.rotX(angularOffset);
			tr.setTranslation(v3);
			tr.mul(tr2);
		}

		if (rotate == ShapeData.ROTATE_Y) {
			displacement = v3.getX()
					- ShapeData.angleOffset(angularOffset, ShapeData.COS);
			v3.setX(displacement);
			displacement = v3.getZ()
					+ ShapeData.angleOffset(angularOffset, ShapeData.SIN);
			v3.setZ(displacement);
			Transform3D tr3 = rod.getTransform3D();
			tr3.rotZ(ShapeData.PERPENDICULAR);
			tr2.rotY(angularOffset);
			tr2.mul(tr3);
			tr.setTranslation(v3);
			tr.mul(tr2);
		}

		if (rotate == ShapeData.ROTATE_Z || rotate == ShapeData.ROTATE_DISABLE) {
			displacement = v3.getZ() + ShapeData.C_OFFSET;
			v3.setZ(displacement);
			tr2 = rod.getTransform3D();
			tr2.rotX(ShapeData.PERPENDICULAR);
			tr.setTranslation(v3);
			tr.mul(tr2);
		}

		BoundingBox bounds = new BoundingBox(new Point3d(-ShapeData.RODRADIUS,
				-(ShapeData.RODLENGTH * 0.355d), -ShapeData.RODRADIUS),
				new Point3d(ShapeData.RODRADIUS,
						(ShapeData.RODLENGTH * 0.355d), ShapeData.RODRADIUS));
		bounds.transform(tr);
		rod.getTransformGroup().setTransform(tr);
		rod.setPositionOfRod(ShapeData.FRONT);
		rod.setAngle(angle);
		rod.setShapeBounds(bounds);
		rod.colorColliding();
		
		return rod;
	}

	/**
	 * Setting the properties for the back rod
	 * @return The back rod
	 */
	private Rod backRod() {
		Rod rod = new Rod(rotate, parentNode);
		Transform3D tr = rod.getTransform3D();
		Transform3D tr2 = rod.getTransform3D();
		Vector3d v3 = new Vector3d();
		double displacement;
		v3.set(origo);

		if (rotate == ShapeData.ROTATE_X) {
			displacement = v3.getY()
					+ ShapeData.angleOffset(angle, ShapeData.SIN);
			v3.setY(displacement);
			displacement = v3.getZ()
					- ShapeData.angleOffset(angle, ShapeData.COS);
			v3.setZ(displacement);
			tr2.rotX(angularOffset);
			tr.setTranslation(v3);
			tr.mul(tr2);
		}

		if (rotate == ShapeData.ROTATE_Y) {
			displacement = v3.getX()
					+ ShapeData.angleOffset(angularOffset, ShapeData.COS);
			v3.setX(displacement);
			displacement = v3.getZ()
					- ShapeData.angleOffset(angularOffset, ShapeData.SIN);
			v3.setZ(displacement);
			Transform3D tr3 = rod.getTransform3D();
			tr3.rotZ(ShapeData.PERPENDICULAR);
			tr2.rotY(angularOffset);
			tr2.mul(tr3);
			tr.setTranslation(v3);
			tr.mul(tr2);
		}

		if (rotate == ShapeData.ROTATE_Z || rotate == ShapeData.ROTATE_DISABLE) {
			displacement = v3.getZ() - ShapeData.C_OFFSET;
			v3.setZ(displacement);
			tr2.rotX(ShapeData.PERPENDICULAR);
			tr.setTranslation(v3);
			tr.mul(tr2);
		}

		BoundingBox bounds = new BoundingBox(new Point3d(-ShapeData.RODRADIUS,
				-(ShapeData.RODLENGTH * 0.355d), -ShapeData.RODRADIUS),
				new Point3d(ShapeData.RODRADIUS,
						(ShapeData.RODLENGTH * 0.355d), ShapeData.RODRADIUS));
		bounds.transform(tr);
		rod.getTransformGroup().setTransform(tr);
		rod.setPositionOfRod(ShapeData.BACK);
		rod.setAngle(angle);
		rod.setShapeBounds(bounds);
		rod.colorColliding();
		
		return rod;
	}

}
